/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.contacts.common.util;

import android.content.Context;
import android.content.res.Resources;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.TtsSpan;
import android.util.Patterns;
import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.dialer.R;
import com.android.dialer.common.LogUtil;
import java.util.Objects;

/** Methods for handling various contact data labels. */
public class ContactDisplayUtils {

  public static final int INTERACTION_CALL = 1;
  public static final int INTERACTION_SMS = 2;

  /**
   * Checks if the given data type is a custom type.
   *
   * @param type Phone data type.
   * @return {@literal true} if the type is custom. {@literal false} if not.
   */
  public static boolean isCustomPhoneType(Integer type) {
    return type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT;
  }

  /**
   * Gets a display label for a given phone type.
   *
   * @param type The type of number.
   * @param customLabel A custom label to use if the phone is determined to be of custom type
   *     determined by {@link #isCustomPhoneType(Integer))}
   * @param interactionType whether this is a call or sms. Either {@link #INTERACTION_CALL} or
   *     {@link #INTERACTION_SMS}.
   * @param context The application context.
   * @return An appropriate string label
   */
  public static CharSequence getLabelForCallOrSms(
      Integer type, CharSequence customLabel, int interactionType, @NonNull Context context) {
    Objects.requireNonNull(context);

    if (isCustomPhoneType(type)) {
      return (customLabel == null) ? "" : customLabel;
    } else {
      int resId;
      if (interactionType == INTERACTION_SMS) {
        resId = getSmsLabelResourceId(type);
      } else {
        resId = getPhoneLabelResourceId(type);
        if (interactionType != INTERACTION_CALL) {
          LogUtil.e(
              "ContactDisplayUtils.getLabelForCallOrSms",
              "un-recognized interaction type: "
                  + interactionType
                  + ". Defaulting to ContactDisplayUtils.INTERACTION_CALL.");
        }
      }

      return context.getResources().getText(resId);
    }
  }

  /**
   * Find a label for calling.
   *
   * @param type The type of number.
   * @return An appropriate string label.
   */
  public static int getPhoneLabelResourceId(Integer type) {
    if (type == null) {
      return R.string.call_other;
    }
    switch (type) {
      case Phone.TYPE_HOME:
        return R.string.call_home;
      case Phone.TYPE_MOBILE:
        return R.string.call_mobile;
      case Phone.TYPE_WORK:
        return R.string.call_work;
      case Phone.TYPE_FAX_WORK:
        return R.string.call_fax_work;
      case Phone.TYPE_FAX_HOME:
        return R.string.call_fax_home;
      case Phone.TYPE_PAGER:
        return R.string.call_pager;
      case Phone.TYPE_OTHER:
        return R.string.call_other;
      case Phone.TYPE_CALLBACK:
        return R.string.call_callback;
      case Phone.TYPE_CAR:
        return R.string.call_car;
      case Phone.TYPE_COMPANY_MAIN:
        return R.string.call_company_main;
      case Phone.TYPE_ISDN:
        return R.string.call_isdn;
      case Phone.TYPE_MAIN:
        return R.string.call_main;
      case Phone.TYPE_OTHER_FAX:
        return R.string.call_other_fax;
      case Phone.TYPE_RADIO:
        return R.string.call_radio;
      case Phone.TYPE_TELEX:
        return R.string.call_telex;
      case Phone.TYPE_TTY_TDD:
        return R.string.call_tty_tdd;
      case Phone.TYPE_WORK_MOBILE:
        return R.string.call_work_mobile;
      case Phone.TYPE_WORK_PAGER:
        return R.string.call_work_pager;
      case Phone.TYPE_ASSISTANT:
        return R.string.call_assistant;
      case Phone.TYPE_MMS:
        return R.string.call_mms;
      default:
        return R.string.call_custom;
    }
  }

  /**
   * Find a label for sending an sms.
   *
   * @param type The type of number.
   * @return An appropriate string label.
   */
  public static int getSmsLabelResourceId(Integer type) {
    if (type == null) {
      return R.string.sms_other;
    }
    switch (type) {
      case Phone.TYPE_HOME:
        return R.string.sms_home;
      case Phone.TYPE_MOBILE:
        return R.string.sms_mobile;
      case Phone.TYPE_WORK:
        return R.string.sms_work;
      case Phone.TYPE_FAX_WORK:
        return R.string.sms_fax_work;
      case Phone.TYPE_FAX_HOME:
        return R.string.sms_fax_home;
      case Phone.TYPE_PAGER:
        return R.string.sms_pager;
      case Phone.TYPE_OTHER:
        return R.string.sms_other;
      case Phone.TYPE_CALLBACK:
        return R.string.sms_callback;
      case Phone.TYPE_CAR:
        return R.string.sms_car;
      case Phone.TYPE_COMPANY_MAIN:
        return R.string.sms_company_main;
      case Phone.TYPE_ISDN:
        return R.string.sms_isdn;
      case Phone.TYPE_MAIN:
        return R.string.sms_main;
      case Phone.TYPE_OTHER_FAX:
        return R.string.sms_other_fax;
      case Phone.TYPE_RADIO:
        return R.string.sms_radio;
      case Phone.TYPE_TELEX:
        return R.string.sms_telex;
      case Phone.TYPE_TTY_TDD:
        return R.string.sms_tty_tdd;
      case Phone.TYPE_WORK_MOBILE:
        return R.string.sms_work_mobile;
      case Phone.TYPE_WORK_PAGER:
        return R.string.sms_work_pager;
      case Phone.TYPE_ASSISTANT:
        return R.string.sms_assistant;
      case Phone.TYPE_MMS:
        return R.string.sms_mms;
      default:
        return R.string.sms_custom;
    }
  }

  /**
   * Whether the given text could be a phone number.
   *
   * <p>Note this will miss many things that are legitimate phone numbers, for example, phone
   * numbers with letters.
   */
  public static boolean isPossiblePhoneNumber(CharSequence text) {
    return text != null && Patterns.PHONE.matcher(text.toString()).matches();
  }

  /**
   * Returns a Spannable for the given message with a telephone {@link TtsSpan} set for the given
   * phone number text wherever it is found within the message.
   */
  public static Spannable getTelephoneTtsSpannable(
      @Nullable String message, @Nullable String phoneNumber) {
    if (message == null) {
      return null;
    }
    final Spannable spannable = new SpannableString(message);
    int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber);
    while (start >= 0) {
      final int end = start + phoneNumber.length();
      final TtsSpan ttsSpan = PhoneNumberUtilsCompat.createTtsSpan(phoneNumber);
      spannable.setSpan(
          ttsSpan,
          start,
          end,
          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // this is consistenly done in a misleading way..
      start = message.indexOf(phoneNumber, end);
    }
    return spannable;
  }

  /**
   * Retrieves a string from a string template that takes 1 phone number as argument, span the
   * number with a telephone {@link TtsSpan}, and return the spanned string.
   *
   * @param resources to retrieve the string from
   * @param stringId ID of the string
   * @param number to pass in the template
   * @return CharSequence with the phone number wrapped in a TtsSpan
   */
  public static CharSequence getTtsSpannedPhoneNumber(
      Resources resources, int stringId, String number) {
    String msg = resources.getString(stringId, number);
    return ContactDisplayUtils.getTelephoneTtsSpannable(msg, number);
  }

  /**
   * Returns either namePrimary or nameAlternative based on the {@link ContactsPreferences}.
   * Defaults to the name that is non-null.
   *
   * @param namePrimary the primary name.
   * @param nameAlternative the alternative name.
   * @param contactsPreferences the ContactsPreferences used to determine the preferred display
   *     name.
   * @return namePrimary or nameAlternative depending on the value of displayOrderPreference.
   */
  public static String getPreferredDisplayName(
      String namePrimary,
      String nameAlternative,
      @Nullable ContactsPreferences contactsPreferences) {
    if (contactsPreferences == null) {
      return namePrimary != null ? namePrimary : nameAlternative;
    }
    if (contactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
      return namePrimary;
    }

    if (contactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE
        && !TextUtils.isEmpty(nameAlternative)) {
      return nameAlternative;
    }

    return namePrimary;
  }

  /**
   * Returns either namePrimary or nameAlternative based on the {@link ContactsPreferences}.
   * Defaults to the name that is non-null.
   *
   * @param namePrimary the primary name.
   * @param nameAlternative the alternative name.
   * @param contactsPreferences the ContactsPreferences used to determine the preferred sort order.
   * @return namePrimary or nameAlternative depending on the value of displayOrderPreference.
   */
  public static String getPreferredSortName(
      String namePrimary,
      String nameAlternative,
      @Nullable ContactsPreferences contactsPreferences) {
    if (contactsPreferences == null) {
      return namePrimary != null ? namePrimary : nameAlternative;
    }

    if (contactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
      return namePrimary;
    }

    if (contactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_ALTERNATIVE
        && !TextUtils.isEmpty(nameAlternative)) {
      return nameAlternative;
    }

    return namePrimary;
  }
}
